from django.shortcuts import render

import redis
import paho.mqtt.client as mqtt
from rest_framework.views import APIView
from rest_framework.response import Response
from datetime import datetime
from rest_framework import status
from .utils import get_tolerance, get_cache_key, get_cache_data, set_cache_data, ensure_db_connection
import json
import requests
import time
import uuid
import logging
import os
from scripts.set_environment import set_environment_based_on_ip,get_local_ip

ip = get_local_ip()

redis_client = redis.Redis(host="10.64.232.15", port=6379, db=0)
logger = logging.getLogger(__name__)

settings_url= os.environ.get("DJANGO_SETTINGS_MODULE")
print(settings_url)
if settings_url == "root_backend.settings_pro":
    print("当前是生产环境")
    from root_backend.settings_pro import WEBHOOK_URL
elif settings_url == "root_backend.settings_dev":
    print("当前是开发环境")
    from root_backend.settings_dev import WEBHOOK_URL
else:
    raise ValueError("未知的配置文件")



def send_message_to_feishu(msg):
    # Webhook 地址（替换成你的真实地址）
    webhook_url = WEBHOOK_URL
    # 请求头
    headers = {"Content-Type": "application/json"}
    # 请求体数据
    data = {"msg_type": "text", "content": {"text": f"信息来源IP：{ip}，数据：{msg}"}}

    try:
        # 发送 POST 请求
        response = requests.post(url=webhook_url, headers=headers, data=json.dumps(data))  # 将字典转换为 JSON 字符串

        # 检查响应状态
        if response.status_code == 200:
            result = response.json()
            if result.get("code") == 0:
                print("消息发送成功")
            else:
                print(f"发送失败，错误信息: {result}")
        else:
            print(f"请求失败，状态码: {response.status_code}")

    except requests.exceptions.RequestException as e:
        print(f"请求异常: {str(e)}")
    except json.JSONDecodeError as e:
        print(f"JSON 解析异常: {str(e)}")
        
class MQTTService:
    def __init__(self):
        send_message_to_feishu(f"测试一下，表明我是来自开发服务器还是生产服务器：我是来自{os.environ.get('DJANGO_SETTINGS_MODULE')}")
        client_id = f"drf_backend_{uuid.uuid4().hex}"
        self.client = mqtt.Client(client_id=client_id)
        self.client.on_connect = self.on_connect
        self.client.on_disconnect = self.on_disconnect
        self.client.on_message = self.on_message
        print(f"当前使用的 client_id 是: {client_id}")

        self.retry_count = 0
        self.max_retries = 10

        while self.retry_count < self.max_retries:
            try:
                print(f"初始化连接MQTT服务器 ({self.retry_count+1}/{self.max_retries})...")
                self.client.connect("10.64.35.32", 1883, 60)
                self.client.loop_start()
                print("连接成功")
                break
            except Exception as e:
                self.retry_count += 1
                print(f"连接失败: {e}")
                if self.retry_count < self.max_retries:
                    time.sleep(2**self.retry_count)  # 指数退避
        if self.retry_count == self.max_retries:
            print("已达到最大连接次数,退出程序")

    def on_connect(self, client, userdata, flags, rc):
        if rc == 0:
            topics = [
                ("EHM/CBM2OP20A/FanucCycle", 2),
                ("EHM/CBM2OP20A/ActionTime", 2),
                ("EHM/FanucAxis/Tempareture", 2),
                ("EHM/FanucCycle/Load", 2),
                ("EHM/AssetState", 2),
                ("EHM/FanucBattery", 2),
                ("PMC/ToolDoorTime", 2),
                ("PMC/Cycletime", 2),
                ("PMC/Change", 2),
                ("PMC/machineFault", 2),
                ("PMC/FaultCode", 2),
            ]
            client.subscribe(topics)
        else:
            print("MQTT服务连接失败")

    def on_disconnect(self, client, userdata, rc):
        print(f"MQTT服务器连接断开,代码: {rc}")
        while self.retry_count < self.max_retries:
            try:
                print(f"尝试重新连接 ({self.retry_count+1}/{self.max_retries})...")
                client.reconnect()
                self.retry_count = 0
                return
            except Exception as e:
                self.retry_count += 1
                time.sleep(2**self.retry_count)  # 指数退避
        print("已达到最大重连次数,退出程序")

    def on_message(self, client, userdata, msg):
        # print(f"Topic: {msg.topic}")
        # print(f"Payload: {msg.payload.decode()}")
        # 写第一个主题的逻辑
        theme_notodo = ["PMC/CycleTime", "PMC/Change", "PMC/FaultCode", "EHM/FanucAxis/Tempareture", "EHM/FanucBattery"]

        if msg.topic == "EHM/CBM2OP20A/FanucCycle":
            # data = msg.payload.decode()
            # message = json.loads(data)
            # self.on_message_type_c(message)
            pass

        # 写第二个主题的数据处理逻辑
        elif msg.topic == "EHM/CBM2OP20A/ActionTime":
            data = msg.payload.decode()
            message = json.loads(data)
            self.on_message_type_a(message)

        elif msg.topic == "PMC/ToolDoorTime":
            data = msg.payload.decode()
            message = json.loads(data)
            self.on_message_type_a(message)

        elif msg.topic == "PMC/Change":
            data = msg.payload.decode()
            message = json.loads(data)
            self.on_message_type_b(message)

        elif msg.topic == "EHM/FanucAxis/Tempareture":
            data = msg.payload.decode()
            message = json.loads(data)
            self.on_message_type_c(message)

        elif msg.topic == "PMC/Cycletime":
            # data = msg.payload.decode()
            # message = json.loads(data)
            # self.on_message_type_b(message)
            pass

        else:
            # print(f"Topic: {msg.topic}")
            # print(f"Payload: {msg.payload.decode()}")
            pass

        # 可以根据需要添加更多主题的处理逻辑

    def publish(self, topic, payload):
        self.client.publish(topic, payload)

    def on_message_type_a(self, message):
        """
        处理消息类型A:
        (1)出现超差，且达到推送报警级别次数，进行第一次推送；
        (2)超差之后如再次出现合格，后续再次超差达到报警级别次数，再次进行推送，以此类推……
        (3)如果连接超差，每天推送一次；
        处理数据例子如下:
        1.topic == "PMC/ToolDoorTime"
        message = {'TimeStamp': '2025-03-10T13:49:16.6794874+08:00', 'EndpointNum': '92201121', 'AssetNum': 'CBM2OP20A', 'Data': 1.3, 'Discription': '刀库门关闭时间'}
        2.topic == "EHM/CBM2OP20A/ActionTime"
        message = {'Timestamp': '2025-03-22T10:17:06.7602673+08:00', 'Name': 'currentHP', 'AssetNum': 'CBM2OP20A', 'EndpointNum': '92201517', 'Location': 'CB2', 'Data': 4.45, 'Tag': None, 'Remark': None, 'CreateDate': '2025-03-22T10:17:06.7602673+08:00'}
        3.topic == "PMC/Cycletime"
        message = { "TimeStamp": "2025-03-22T14:39:42.7629681+08:00", "EndpointNum": "2090100", "AssetNum": "ASOP2090", "Data": 279.0, "UnloadTime": 0.0, "LoadTime": 0.0, "Total": 279.0, "OverData": 232.0, "Tag": null, "Status": "INVALID", "CreateDate": "0001-01-01T00:00:00", "ModifyDate": null }
        """
        asset_num = message.get("AssetNum")
        data = message.get("Data")
        discription = message.get("Discription")
        name = message.get("Name")
        time = self.convert_time_datatime(message.get("TimeStamp", message.get("Timestamp")))

        if not (asset_num and data and (discription or name)):
            return  # 消息格式不完整

        if asset_num.startswith(("AS", "CS")):
            zone, op = asset_num[:2], asset_num[2:]
        else:
            zone, op = asset_num[:4], asset_num[4:]

        if discription == "刀库门打开时间":
            item = "ToolDoor_Open"
        elif discription == "刀库门关闭时间":
            item = "ToolDoor_Close"
        else:
            item = name

        # 从缓存中获取公差范围
        lower_limit, upper_limit, item_cn, item_level = get_tolerance(zone, op, item)
        if lower_limit is None or upper_limit is None or item_level is None:
            return  # 无法找到公差范围及公差级别

        cache_key = get_cache_key(zone, op, item)
        alert_threshold = item_level
        # print(cache_key)

        # 检查是否超差
        is_overproof = self.data_overproof_check(data, lower_limit, upper_limit)

        # 如果合格，重置计数、标记合格状态、重置上次推送时间
        if not is_overproof:
            set_cache_data(redis_client, f"{cache_key}_count", None)
            set_cache_data(redis_client, f"{cache_key}_qualified", "true")
            set_cache_data(redis_client, f"{cache_key}_last_push_date", None)
            return

        # 如果超差往下执行
        warn_message = f"{time},{asset_num}-{item_cn}数据超差：{data},公差：{lower_limit}-{upper_limit}"
        logger.warning(warn_message)

        lock_key = f"{cache_key}_lock"  # 获取分布式锁
        try:
            lock_acquired = redis_client.setnx(lock_key, "locked")
            if not lock_acquired:
                return  # 未获取到锁，直接返回
        except Exception as e:
            print(f"Error acquiring lock: {e}")
            return

        try:
            # 获取缓存中的计数
            count_str = get_cache_data(redis_client, f"{cache_key}_count")
            count = int(count_str) if count_str else 0

            # 获取数据是否合格过的标记
            qualified = get_cache_data(redis_client, f"{cache_key}_qualified")
            if qualified == "true":
                count = 0  # 数据曾经合格过，重置计数
                set_cache_data(redis_client, f"{cache_key}_qualified", None)

            # 增加计数
            count += 1
            set_cache_data(redis_client, f"{cache_key}_count", count)

            # 阈值判断
            if count >= alert_threshold:
                last_push_date_str = get_cache_data(redis_client, f"{cache_key}_last_push_date")
                last_push_date = None
                if last_push_date_str:
                    try:
                        last_push_date = datetime.fromisoformat(last_push_date_str).date()
                    except:
                        pass

                today = datetime.now().date()

                print(f"连续超差{count}次，报警级别{alert_threshold},上次推送日期{last_push_date}")

                # 推送条件判断
                if last_push_date != today:
                    # 执行数据存储
                    data_dict = {
                        "zone": zone,
                        "op": op,
                        "item": item,
                        "value": data,
                        "item_cn": item_cn or item,
                        "item_level": item_level,
                        "upper_limit": upper_limit,
                        "lower_limit": lower_limit,
                        "timestamp": time,
                    }
                    self.data_overproof_save(data_dict)

                    # 飞书报警
                    warn_msg = f"【EPHM-Redis报警推送测试】{time} {asset_num}-{item_cn}值{data}，" f"超差范围({lower_limit},{upper_limit})，当前连续超差{count}次"
                    send_message_to_feishu(warn_msg)
                    print(warn_msg)

                    # 状态更新
                    set_cache_data(redis_client, f"{cache_key}_last_push_date", today.isoformat())
                    set_cache_data(redis_client, f"{cache_key}_count", 0)  # 重置计数器
        except Exception as e:
            print(f"处理超标数据时出错: {e}")
        finally:
            # 释放锁
            try:
                redis_client.delete(lock_key)
            except Exception as e:
                print(f"释放锁出错: {e}")

    def on_message_type_b(self, message):
        """
        处理消息类型B:
        (1)出现超差；
        (2)达到推送报警级别次数；
        (3)进行推送报警。
        数据例子如下:
        1.topic == "PMC/Change"
        message = {'Id': 0, 'AssetNum': 'CBM2OP70A', 'ToolNumber': 22011, 'StartTime': '2025-03-10T14:44:19.3806032+08:00', 'EndTime': '2025-03-10T14:49:33.3916751+08:00', 'Duration': 314.0, 'Location': 'CB2', 'OP': 'CBM2OP70A', 'OldMaxLife': 0, 'NewMaxLife': 0, 'OldActualLife': 0, 'NewActualLife': 0, 'OldRestLife': 0, 'NewRestLife': 0, 'OldBlockNumber': None, 'NewBlockNumber': None, 'CreateDate': '2025-03-10T14:49:33.3916751+08:00'}
        """
        try:
            asset_num = message.get("AssetNum")
            duration = message.get("Duration")
            tool_number = message.get("ToolNumber")
            time = self.convert_time_datatime(message.get("CreateDate", message.get("EndTime")))

            if not (asset_num and duration and tool_number):
                return  # 消息格式不完整

            if asset_num.startswith(("AS", "CS")):
                zone, op = asset_num[:2], asset_num[2:]
            else:
                zone, op = asset_num[:4], asset_num[4:]

            item = tool_number

            # 从缓存中获取公差范围
            # lower_limit, upper_limit, item_cn, item_level = get_tolerance(zone, op, item)
            lower_limit, upper_limit, item_cn, item_level = get_tolerance("CBM2", "OP180A", "22219")
            if lower_limit is None or upper_limit is None:
                return  # 无法找到公差范围

            cache_key = get_cache_key(zone, op, item)

            warn_msg = f"{time},{asset_num}-{tool_number}换刀时间：{duration},公差：{lower_limit}-{upper_limit}"
            logger.warning(warn_msg)

            # 检查是否超差
            if not self.data_overproof_check(duration, lower_limit, upper_limit):
                set_cache_data(redis_client, cache_key, None)
                return

            # 获取分布式锁
            lock_key = f"{cache_key}_lock"
            try:
                lock_acquired = redis_client.setnx(lock_key, "locked")
                if not lock_acquired:
                    return  # 未获取到锁，直接返回
            except Exception as e:
                print(f"Error acquiring lock: {e}")
                return

            try:
                # 获取缓存中的计数
                count_str = get_cache_data(redis_client, f"{cache_key}_count")
                count = int(count_str) if count_str else 0

                # 增加计数
                count += 1
                set_cache_data(redis_client, f"{cache_key}_count", count)

                # 检查是否达到超差次数
                if count < item_level:
                    return

                # 更新缓存状态和时间戳
                set_cache_data(redis_client, cache_key, datetime.now().isoformat())

                # 执行保存超差及推送逻辑
                data_dict = {
                    "zone": zone,
                    "op": op,
                    "item": item,
                    "value": duration,
                    "item_cn": item_cn or item,
                    "item_level": item_level,
                    "upper_limit": upper_limit,
                    "lower_limit": lower_limit,
                    "timestamp": time,
                }
                self.data_overproof_save(data_dict)
                warn_info = f"【EPHM-Redis报警推送测试】{time},{asset_num}-{item_cn or item}值:{duration},公差为{(lower_limit, upper_limit)},数据超差，请关注！"
                print(warn_info)
                send_message_to_feishu(warn_info)
            except Exception as e:
                print(f"处理超标数据时出错: {e}")

            finally:
                # 释放锁
                try:
                    redis_client.delete(lock_key)
                except Exception as e:
                    print(f"释放锁出错: {e}")

        except json.JSONDecodeError:
            print("Failed to decode JSON from the message payload.")
        except Exception as e:
            print(f"An error occurred: {e}")

    def on_message_type_c(self, message):
        """
        推送逻辑同类型A,但处理数据为一条信息中多项数据。
        topic = 'EHM/CBM2OP20A/FanucCycle'
        message = {'Timestamp': '2025-03-22T12:09:29.0582914+08:00', 'Name': 'CBM2OP20A-20250322120650', 'AssetNum': 'CBM2OP20A', 'Location': 'CB2', 'SpindleLoad': 0.0, 'LoadX': 5.0, 'LoadY': 38.0, 'LoadZ': 8.0, 'LoadA': 0.0, 'LoadB': 0.0, 'CodeLine': 'N600 M113 G53 Z617.000', 'ToolNum': '22223', 'ToolLife': '183', 'Coolant': 0.0}
        Topic: EHM/FanucAxis/Tempareture
        { "Timestamp": "2025-03-22T14:30:30.0860321+08:00", "AssetNum": "CBM2OP20A", "Channel": "Tempareture", "Location": "CB2", "Tag": "", "S1": 45.0, "X1": 32.0, "Y1": 51.0, "Z1": 32.0, "B1": 0.0, "A1": 0.0, "W1": 28.0, "V1": 32.0, "U1": 24.0, "VP": 32.0, "CreateDate": "2025-03-22T14:30:30.0860321+08:00" }
        """
        try:
            asset_num = message.get("AssetNum")
            channel = message.get("Channel")
            time = self.convert_time_datatime(message.get("Timestamp"))

            if not asset_num:
                return  # 消息格式无模块工位信息

            if asset_num.startswith(("AS", "CS")):
                zone, op = asset_num[:2], asset_num[2:]
            else:
                zone, op = asset_num[:4], asset_num[4:]

            if channel:
                items_to_check = ["S1", "X1", "Y1", "Z1"]
            elif message.get("SpindleLoad"):
                items_to_check = ["SpindleLoad", "LoadX", "LoadY", "LoadZ", "LoadA", "LoadB"]
            else:
                return

            for item in items_to_check:
                value = message.get(item)
                if value is None:
                    continue

                item_check = f"Tempareture_{item}"

                # lower_limit, upper_limit, item_cn, item_level = get_tolerance(zone, op, item)
                lower_limit, upper_limit, item_cn, item_level = get_tolerance("CBM2", "OP20A", item_check)
                if lower_limit is None or upper_limit is None:
                    continue

                cache_key = get_cache_key(zone, op, item_check)
                alert_threshold = item_level

                # 如果合格，重置计数、标识合格、重置上次推送日期
                if not self.data_overproof_check(value, lower_limit, upper_limit):
                    set_cache_data(redis_client, f"{cache_key}_count", None)
                    set_cache_data(redis_client, f"{cache_key}_qualified", "true")
                    set_cache_data(redis_client, f"{cache_key}_last_push_date", None)
                    continue

                # 如果超差，往下处理
                warn_msg = f"{time},{asset_num}-{item_cn}数据超差：{value},公差：{lower_limit}-{upper_limit}"
                logger.warning(warn_msg)

                lock_key = f"{cache_key}_lock"  # 获取分布式锁
                try:
                    lock_acquired = redis_client.setnx(lock_key, "locked")
                    if not lock_acquired:
                        continue  # 未获取到锁跳过处理
                except Exception as e:
                    print(f"获取锁失败: {e}")
                    continue

                try:
                    # 获取缓存状态
                    count_str = get_cache_data(redis_client, f"{cache_key}_count")
                    count = int(count_str) if count_str else 0
                    qualified = get_cache_data(redis_client, f"{cache_key}_qualified")

                    # 合格状态重置逻辑
                    if qualified == "true":
                        count = 0
                        set_cache_data(redis_client, f"{cache_key}_qualified", None)

                    # 更新计数器
                    count += 1
                    set_cache_data(redis_client, f"{cache_key}_count", count)

                    # 超差判断与推送处理
                    if count >= alert_threshold:
                        last_push_date_str = get_cache_data(redis_client, f"{cache_key}_last_push_date")
                        last_push_date = None
                        if last_push_date_str:
                            try:
                                last_push_date = datetime.fromisoformat(last_push_date_str).date()
                            except:
                                pass

                        today = datetime.now().date()
                        if last_push_date != today:
                            # 执行报警推送
                            data_dict = {
                                "zone": zone,
                                "op": op,
                                "item": item,
                                "value": value,
                                "item_cn": item_cn or item,
                                "item_level": item_level,
                                "upper_limit": upper_limit,
                                "lower_limit": lower_limit,
                                "timestamp": time,
                            }
                            self.data_overproof_save(data_dict)
                            warn_info = f"【EPHM-Redis报警推送测试】{time},{asset_num}-{item_cn}值:{value},公差为{(lower_limit, upper_limit)}，当前连续超差{count}次"
                            print(warn_info)
                            send_message_to_feishu(warn_info)

                            # 更新状态
                            set_cache_data(redis_client, f"{cache_key}_last_push_date", today.isoformat())
                            set_cache_data(redis_client, f"{cache_key}_count", 0)  # 重置计数器

                except Exception as e:
                    print(f"处理{item}时出错: {e}")

                finally:
                    try:
                        redis_client.delete(lock_key)
                    except Exception as e:
                        print(f"释放锁失败: {e}")

        except Exception as e:
            print(f"消息处理异常: {e}")

    def data_overproof_check(self, data, lower_limit, upper_limit):
        """
        检查数据是否超差
        """
        return data < lower_limit or data > upper_limit

    @ensure_db_connection
    def data_overproof_save(self, data: dict):
        """
        接收data_dict信息,保存数据到ephm_limit_warining表;
        """
        from ephm_api.models import EphmLimitsWarning

        try:
            create_time = datetime.now()

            zone = data.get("zone")
            op = data.get("op")
            item = data.get("item")
            item_cn = data.get("item_cn")
            item_level = data.get("item_level")
            value = data.get("value")
            upper_limit = data.get("upper_limit")
            lower_limit = data.get("lower_limit")
            timestamp = data.get("timestamp")

            warning_record = EphmLimitsWarning(
                zone=zone,
                op=op,
                item=item,
                item_cn=item_cn,
                item_level=item_level,
                value=value,
                up_limit=upper_limit,
                low_limit=lower_limit,
                timestamp=timestamp,
                create_time=create_time,
            )
            warning_record.save()
        except Exception as e:
            print(f"保存警告记录时出错: {e}")

    def convert_time_datatime(self, time_str):
        """
        时间格式处理,将类似'2025-03-04T12:42:20.0050698+08:00'时间处理为datatime格式
        """
        main_part = time_str[:19]
        format_str = "%Y-%m-%dT%H:%M:%S"
        dt = datetime.strptime(main_part, format_str)
        return dt


class DeviceControlView(APIView):
    def post(self, request):
        device_id = request.data.get("device_id")
        command = request.data.get("command")
        # 调用 MQTT 服务发布指令
        MQTTService().publish(f"device/{device_id}/commands", command)
        return Response({"status": "command_sent"})
